/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package searcherPackage;

import java.util.HashSet;
import java.util.Set;
import java.util.Stack;
import searcherPackage.Index.Index;
import searcherPackage.Index.InvalidTermException;
import searcherPackage.Index.PostingsList;
import searcherPackage.Index.Term;

/**
 * @author i_khri
 */
public class QueryMatcher {

    public QueryMatcher(Set<String> operators, Index tokensMap){
        this.operators.addAll(operators);
        this.tokensMap = tokensMap;
    }



    public PostingsList matchQuery(String theQuery){
        theQuery = theQuery.trim();
        if(theQuery.isEmpty() || tokensMap.isEmpty()){
            return new PostingsList();
        }

        char queryChars[] = theQuery.toCharArray();
        String token = "";
        int i = 0;
        stepOver = 0;
        char c;

        Stack<String> operatorsStack = new Stack<String>();
        Stack<PostingsList> operandsStack = new Stack<PostingsList>();

        while(i < queryChars.length){
            c = queryChars[i];
            if(c == ')'){
                break;
            }else if(c == '('){
                operandsStack.push(matchQuery(theQuery.substring(stepOver+1)));
                stepOver++;
                i += stepOver;
            }else if(c == ' ' && !token.isEmpty()){
                if(operators.contains(token)){
                    operatorsStack.push(token);
                }else{
                    operandsStack.push(valueOf(token));
                }
                token = "";
            }else if(c != ' '){
                token += c;
            }

            stepOver++;
            i++;
        }

        if(!token.isEmpty()){
            operandsStack.push(valueOf(token)); // assuming a query does not end with an operator.
        }

        return computeQuery(operandsStack, operatorsStack);
    }





    private PostingsList valueOf(String token){
        Stemmer stemmer = new Stemmer();
        stemmer.add(token.toLowerCase().toCharArray(), token.toCharArray().length);
        stemmer.stem();
        token = stemmer.toString();
        PostingsList pl = new PostingsList();
        try{
            pl = tokensMap.getPostingsList(Term.newInstance(token));
        }catch(InvalidTermException ignore){}
        return pl;
    }



    private PostingsList computeQuery(Stack<PostingsList> operands, Stack<String> operators){
        while(!operators.empty()){
            String operator = operators.pop();
            if(binary(operator)){
                operands.push(apply(operator, operands.pop(), operands.pop()));
            }
        }
        return operands.pop();
    }



    private boolean binary(String operator){
        return operator.equals("AND") || operator.equals("OR");
    }



    private PostingsList apply(String operator, PostingsList op1, PostingsList op2){
        if(operator.equals("AND")){
            return PostingsList.intersection(op1, op2);
        }else if(operator.equals("OR")){
            return PostingsList.union(op1, op2);
        }else{
            throw new UnsupportedOperationException("QueryProcessor-apply() : operator is [" + operator + "]");
        }
    }



    private int stepOver = 0;
    private Set<String> operators = new HashSet<String>();
    private Index tokensMap = new Index();
}
